﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Collections;
using Engine.Objects;
namespace Engine.Voxel
{
   public class VoxelObject:DrawableGameComponent,Interface.IEngineObject
    {
        string o_Name;
        Vector3 o_Position;
        Quaternion o_Rotation;
        Matrix o_Transform;
        Vector3 o_Scale;
        Engine.MarchCube.TRIANGLE[] tri;
        Engine.MarchCube.TRIANGLE[] tri2;
        BaseMaterial _material;
        BitArray _objData;
        BasicEffect _effect;
        VertexBuffer _VB;
        IndexBuffer _IB;
        bool _needRebild;
        public string Name
        {
            get
            {
                return o_Name;
            }
            set
            {
                
            }
        }

        public bool isGUI
        {
            get { return false; }
        }
        
        public Vector3 Position
        {
            get
            {
                return o_Position;
            }
            set
            {
                o_Position=value;
            }
        }

        public Quaternion Rotation
        {
            get
            {
                return o_Rotation;
            }
            set
            {
                o_Rotation = value;
            }
        }

        public Matrix Transform
        {
            get
            {
                return o_Transform;
            }
            set
            {
                o_Transform = value;
            }
        }

        public Vector3 Scale
        {
            get
            {
                return o_Scale;
            }
            set
            {
                o_Scale = value;
            }
        }
        double[, ,] Gdata;
        private float GetVal(int x, int y, int z, int i)
        {
            switch (i)
            {
                case 0:
                    return (float)Gdata[x, y, z];
                case 1:
                    return (float)Gdata[x + 1, y, z];
                case 2:
                    return (float)Gdata[x + 1, y + 1, z];
                case 3:
                    return (float)Gdata[x, y + 1, z];
                case 4:
                    return (float)Gdata[x, y, z + 1];
                case 5:
                    return (float)Gdata[x + 1, y, z + 1];
                case 6:
                    return (float)Gdata[x + 1, y + 1, z + 1];
                case 7:
                    return (float)Gdata[x, y + 1, z + 1];
            }
            return 0;
        }
        public VoxelObject(NOWAEngine _game,string _name):this(_game,_name,Vector3.Zero,Vector3.One,Quaternion.Identity)
        {
           
        }
        public unsafe VoxelObject(NOWAEngine _game, string _name, Vector3 pos, Vector3 scal, Quaternion rot)
            : base(_game)
        {
            o_Name = _name;
            _needRebild = true;
            o_Position = pos;
            o_Rotation = rot;
            o_Scale = scal;
            _objData = new BitArray(32 * 32 * 32,false);
            Random rnd = new Random(123154512);
            MarchCube.SimplexNoise noise = new MarchCube.SimplexNoise();
            Gdata = noise.Generate3D(33, 33, 33, (int)pos.X, (int)pos.Y, (int)pos.Z);
           tri = null;
           // 
           List<MarchCube.TRIANGLE> tmp = new List<MarchCube.TRIANGLE>(0);
          for (int x1 = 0; x1 < 32; x1++)
            {

                for (int y1 = 0; y1 < 32; y1++)
                {

                    for (int z1 = 0; z1 < 32; z1++)
                    
                    {
                        int w=rnd.Next(0, 2) ;
                    w = 1;
                        if (w>0)
                        {
                            Engine.MarchCube.GRIDCELL gc = Engine.MarchCube.MarchingCubes.GetBaseGrid(new Vector3(x1, y1, z1));
                            for (int i = 0; i < 8; i++)
                            {
                                gc.val[i] = GetVal(x1, y1, z1, i);
                            }
                            tri = null;
                            int count = Engine.MarchCube.MarchingCubes.Polygonise(gc, 0.6f, ref tri);
                            
                            if (tri!=null)
                            {
                                tmp.AddRange(tri);
                            }
                            
                          //  _objData[x1 * (10*10) + y1 * 10 + z1] = true;
                        }
                       
                    }
                }
            }
          tri = tmp.ToArray();
            _effect = new BasicEffect(Game.GraphicsDevice);
          //  Engine.MarchCube.GRIDCELL gc = Engine.MarchCube.MarchingCubes.GetBaseGrid(new Engine.MarchCube.XYZ(0, 0, 0));
          
          
          //  //for (int z = 0; z < 2; z++)
          //  //        { 
          //  //    for (int y = 0; y < 2; y++)
          //  //    {
          //  //for (int x = 0; x < 2; x++)
          //  //{
               
          //  //            int index = (z * 4) + y * 2 + x;
          //  //          //  gc.p[index] = new Engine.MarchCube.XYZ() { x = x, y = y, z = z };
          //  //           // gc.val[index] = rnd.NextDouble();
          //  //            gc.val[index] = 0;
          //  //        }
          //  //    }
          //  //}
          //  gc.val[0] = 0.1;
          //  gc.val[1] = 0.5;
          //  gc.val[2] = 0.5;
          //  gc.val[3] = 1;
          //  gc.val[4] = 0.1;
          //  gc.val[5] = 0.5;
          //  gc.val[6] = 0.5;
          //  gc.val[7] = 0.1;
          ////  (gc.p) = &points;
          //  tri = null;
          //  int count = Engine.MarchCube.MarchingCubes.Polygonise(gc, 0.5, ref tri);
            tri2 = null;
            //gc = Engine.MarchCube.MarchingCubes.GetBaseGrid(new Engine.MarchCube.XYZ(1, 0, 0));
            //gc.val[0] = 0.1;
            //gc.val[1] = 0.5;
            //gc.val[2] = 0.5;
            //gc.val[3] = 1;
            //gc.val[4] = 0.1;
            //gc.val[5] = 0.5;
            //gc.val[6] = 0.5;
            //gc.val[7] = 0.1;
            // count = Engine.MarchCube.MarchingCubes.PolygoniseTri(gc,0.5, ref tri2,0,1,0,1);
        }
        public bool this[int index]
        {
            get { return _objData[index]; }
            set { _objData[index] = value;
            _needRebild = true;
            }
        }
        public override void Update(GameTime gameTime)
        {
            if (_needRebild)
            {
                Rebild();
                _needRebild = false;
            }

             base.Update(gameTime);
        }

        private void Rebild()
        {
            DataVBIB dat = ChunkBilder.BildMesh(_objData, 10, 10, 10, 1, Vector3.Zero);


            if (dat._indeces.Length > 0)
            {

                VertexPositionColor[] vertex = dat._vertrices;
                short[] triangle = dat._indeces;
                _IB = new IndexBuffer(Game.GraphicsDevice, typeof(short), triangle.Length, BufferUsage.None);
                _IB.SetData(triangle);
                _VB = new VertexBuffer(Game.GraphicsDevice, vertex[0].GetType(), vertex.Length, BufferUsage.None);
                _VB.SetData(vertex);
            }
        }
        public override void Draw(GameTime gameTime)
        { 
            NOWAEngine nn = Game as NOWAEngine;
                DEFAULTCamera cam = (DEFAULTCamera)(((Engine.NOWAEngine)Game).Manager.GetComponent("CameraManager")).GetMaincamera(); 
            //  rast.FillMode = FillMode.WireFrame;
             //   rast.CullMode = CullMode.CullCounterClockwiseFace;

            RasterizerState rast = new RasterizerState();
            GraphicsDevice.BlendState = BlendState.Opaque;
            GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            _effect.View = cam.ViewMatrix;
            _effect.Projection = cam.ProjectionMatrix;

            //_effect.World = Matrix.Identity;
            _effect.VertexColorEnabled = true;
            if (!_needRebild && _VB != null)
            {


               
             
            

                //DepthStencilState ds = GraphicsDevice.DepthStencilState;
                //BlendState bs = GraphicsDevice.BlendState;
                ////устанавливаем новые значения 
                //GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
                //GraphicsDevice.BlendState = BlendState.AlphaBlend;
                //// тут рисуем..   
                //// восстанавливаем настройки из ранее сохраненных
                //GraphicsDevice.DepthStencilState = ds;
                //GraphicsDevice.BlendState = bs;

              
              //  GraphicsDevice.RasterizerState = rast;
             //   GraphicsDevice.DepthStencilState = DepthStencilState.Default;  
               
             //   nn.GraphicsDevice.DepthStencilState = DepthStencilState.None;
                nn.GraphicsDevice.SetVertexBuffer(_VB);
                nn.GraphicsDevice.Indices = _IB;

                foreach (EffectPass pass in _effect.Techniques[0].Passes)
                {
                    pass.Apply();
                    nn.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, _VB.VertexCount, 0, _IB.IndexCount / 3);

                }
               
                //VertexPositionColor[] tmp = new VertexPositionColor[]
                //{
                //   new VertexPositionColor(new Vector3((float)(tri[0].p[0].x),(float)(tri[0].p[0].y),(float)(tri[0].p[0].z)),Color.Red),
                //    new VertexPositionColor(new Vector3((float)(tri[0].p[1].x),(float)(tri[0].p[1].y),(float)(tri[0].p[1].z)),Color.Blue),
                //     new VertexPositionColor(new Vector3((float)(tri[0].p[2].x),(float)(tri[0].p[2].y),(float)(tri[0].p[2].z)),Color.Green)
                //};
               
              //  nn.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            }
            if (tri != null)
            {
               // Matrix oldW = _effect.World;
               // _effect.World = Matrix.Identity;
                _effect.Techniques[0].Passes[0].Apply();

                VertexPositionColor[] tmp = new VertexPositionColor[tri.Length * 3];
                short[] indTmp = new short[tri.Length * 3];
                int c = 0;
                for (int i = 0; i < tri.Length; i++)
                {
                    indTmp[c] = (short)c;
                    indTmp[c + 1] = (short)(c + 1);
                    indTmp[c + 2] = (short)(c + 2);
                    tmp[c++] = new VertexPositionColor(new Vector3((float)(tri[i].p[0].X), (float)(tri[i].p[0].Y), (float)(tri[i].p[0].Z)), Color.Gray);
                    tmp[c++] = new VertexPositionColor(new Vector3((float)(tri[i].p[1].X), (float)(tri[i].p[1].Y), (float)(tri[i].p[1].Z)), Color.Brown);
                    tmp[c++] = new VertexPositionColor(new Vector3((float)(tri[i].p[2].X), (float)(tri[i].p[2].Y), (float)(tri[i].p[2].Z)), Color.Green);

                }
                // short[] indTmp = new short[] { 0, 1, 2 };

                nn.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, tmp, 0, tmp.Length, indTmp, 0, indTmp.Length / 3);
              //  _effect.World = oldW;
            }

            if (tri2 != null)
            {
                Matrix oldW = _effect.World;
                _effect.World = Matrix.Identity;
                _effect.Techniques[0].Passes[0].Apply();

                VertexPositionColor[] tmp = new VertexPositionColor[tri2.Length * 3];
                short[] indTmp = new short[tri2.Length * 3];
                int c = 0;
                for (int i = 0; i < tri2.Length; i++)
                {
                   
                    
                    indTmp[c] = (short)c;
                    indTmp[c + 1] = (short)(c + 1);
                    indTmp[c + 2] = (short)(c + 2);
                    tmp[c++] = new VertexPositionColor(new Vector3((float)(tri2[i].p[0].X), (float)(tri2[i].p[0].Y), (float)(tri2[i].p[0].Z)), Color.Red);
                    tmp[c++] = new VertexPositionColor(new Vector3((float)(tri2[i].p[1].X), (float)(tri2[i].p[1].Y), (float)(tri2[i].p[1].Z)), Color.Blue);
                    tmp[c++] = new VertexPositionColor(new Vector3((float)(tri2[i].p[2].X), (float)(tri2[i].p[2].Y), (float)(tri2[i].p[2].Z)), Color.Green);
                    
                }
                // short[] indTmp = new short[] { 0, 1, 2 };

                nn.GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, tmp, 0, tmp.Length, indTmp, 0, indTmp.Length / 3);
                _effect.World = oldW;
            }
            base.Draw(gameTime);
        }
        public new void LoadContent()
        {
            _effect.World = Matrix.Identity * Matrix.CreateScale(Scale) * Matrix.CreateFromQuaternion(o_Rotation) * Matrix.CreateTranslation(o_Position);
        }

    }
}
